package com.block.product.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.block.common.annotate.RedisLock;
import com.block.common.constant.RedisKeyConstant;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.dto.UserInfoDTO;
import com.block.common.service.IProductCommonService;
import com.block.common.service.LogFactory;
import com.block.db.entity.ProductInfo;
import com.block.db.entity.ProductProfit;
import com.block.db.entity.ProductPush;
import com.block.db.mapper.ProductProfitMapper;
import com.block.db.mapper.ProductPushMapper;
import com.block.product.enums.ProdPriceTypeEnum;
import com.block.product.service.IProductProfitAddupService;
import com.block.product.service.IProductProfitService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 
 * @since 2022-08-30
 */
@Service
public class ProductProfitServiceImpl extends ServiceImpl<ProductProfitMapper, ProductProfit> implements IProductProfitService {

    private static Logger log = LogFactory.getCommLog();

    @Autowired
    IProductProfitAddupService productProfitAddupService;
    @Resource
    ProductPushMapper productPushMapper;
    @Autowired
    IProductCommonService productCommonService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    NotifyExceptionService notifyExceptionService;

    /**
     * 计算产品 uv 和 收益
     * 普通产品和联登产品
     *
     * 保存产品收益信息
     */
    @Async
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public void saveProductUvProfit(Long productId, UserInfoDTO userInfoDTO, String sceneId)  {

        String dateInfo = LocalDate.now().toString();
        try {

            // 查询产品信息
            ProductInfo productInfo = productCommonService.getProductInfoCache(String.valueOf(productId));
            if (ObjectUtil.isEmpty(productInfo)){
                throw new RuntimeException(StrUtil.format("产品信息为空【】{}", productId));
            }

            // 查询今日产品uv/收益信息
            ProductProfit productProfit = this.baseMapper.findOneByDateInfoAndProductIdLock(dateInfo, productInfo.getId());
            if (ObjectUtil.isEmpty(productProfit)){
                String lockKey = dateInfo + ":" + productId;
//                productProfit = ((IProductProfitService) AopContext.currentProxy()).saveTodayProdProfitInfo(lockKey, dateInfo, productInfo);
                productProfit = this.saveTodayProdProfitInfo(lockKey, dateInfo, productInfo);

                if (ObjectUtil.isEmpty(productProfit)){
                    throw new RuntimeException(StrUtil.format("新增产品今日收益异常【dateInfo】{}【productId】{}", dateInfo, productId));
                }
            }

            Integer newUvNumber = productProfit.getUvNumber() + 1;
            BigDecimal profitAmount = productProfit.getProfitAmount();
            if (ObjectUtil.equal(ProdPriceTypeEnum.UV.getValue(), productInfo.getPriceType())){
                // uv profit
                profitAmount = NumberUtil.add(profitAmount, productInfo.getPriceAmount());
            }
            // 联登产品和A产品不统计收益

            Integer reduceNumber = productProfit.getDayUvNumber() - newUvNumber;
            String redisKey = StrUtil.format(RedisKeyConstant.PRODUCT_DAY_UV_COUNT_KEY, dateInfo, productId);
            // 更新产品的uv缓存信息
            stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(reduceNumber), 1L, TimeUnit.DAYS);

            ProductProfit updateEntry = new ProductProfit();
            updateEntry.setId(productProfit.getId());
            updateEntry.setProfitAmount(profitAmount);
            updateEntry.setUvNumber(newUvNumber);

            boolean updateRes = this.updateById(updateEntry);
            log.info("【保存产品收益信息】更新产品收益信息【updateRes】{}【dateInfo】{}【productId】{}",updateRes, dateInfo, productInfo.getId());

            // 保存收益历史
            boolean saveRes = productProfitAddupService.saveProdProfitAddupInfo(productInfo, dateInfo, userInfoDTO, sceneId);
            log.info("【保存产品收益信息】保存产品收益信息历史【saveRes】{}【dateInfo】{}【productId】{}【uid】{}",
                    saveRes, dateInfo, productInfo.getId(), userInfoDTO.getUid());
        }catch (Exception exception){
            log.error("【保存产品收益信息】处理出现异常", exception);
            notifyExceptionService.notifyNoticeInfo(
                    "PRODUCT_UV_SAVE",
                    "保存产品收益信息异常",
                    StrUtil.format("{}|{}|", productId, JSON.toJSONString(userInfoDTO)),
                    exception );
        }
    }


    /**
     * 保存产品联登收益
     * @param productId
     * @param userInfoDTO
     */
    public void saveProductUnionProfit(Long productId, UserInfoDTO userInfoDTO)  {

        // TODO: 2023/4/13 产品联登收益 统计

    }

    /**
     * 根据产品信息获取
     * @param productInfo
     * @return
     */
    @Override
    @RedisLock(lockName = "PRODUCT::PROFIT::LOCK::", key = "#lockKey" )
//    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public ProductProfit saveTodayProdProfitInfo(String lockKey, String dateInfo, ProductInfo productInfo){

        ProductPush productPush = productPushMapper.selectByProductId(productInfo.getId());

        ProductProfit productProfit = new ProductProfit();
        productProfit.setProductId(productInfo.getId());
        productProfit.setProductName(productInfo.getProductName());
        productProfit.setDateInfo(dateInfo);
        productProfit.setProfitAmount(BigDecimal.ZERO);
        productProfit.setUvNumber(0);
        productProfit.setDayUvNumber(ObjectUtil.isEmpty(productPush)?9999: productPush.getDayUvNumber());

        boolean saveRes = this.save(productProfit);
        if (!saveRes){
            log.error("【保存产品收益信息】新增产品收益记录失败【dateInfo】{}【productId】{}", dateInfo, productInfo.getId());
            return null;
        }
        return productProfit;
    }




}
